/* ===========================================
	date			author		amend
	4/27/2010		jgx			create file
	4/27/2010		jgx			create the six be_* class
============================================= */

#ifndef _bencoding_h_
#define _bencoding_h_

#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>
#include <map>
using namespace std;
namespace tinygoldmine {

typedef enum {
	BE_STR,
	BE_INT,
	BE_LIST,
	BE_DICT,
} be_type;

class be_object;
class be_factory
{
public:
	static be_object* create_be_object(const char* s_in);
};

class be_object
{
protected:
	be_type type_;
public:
// 	be_object() {}
// 	be_object(be_type type) : type_(type){}
	virtual void dump(){}
	virtual bool be_decode(const char* s_in, char** s_next) = 0;
	virtual bool be_encode(char* s_out, int* len) = 0;
};

class be_integer : public be_object
{
public:
	long long elem_;
public:
	be_integer(){ type_ = BE_INT; }
	be_integer(long long l) : elem_(l) { type_ = BE_INT; }
	bool be_decode(const char* s_in, char** s_next)
	{
		if(!s_in || !s_next) return false;

		elem_ = strtol(s_in, s_next, 10);

		if (**s_next++ != 'e') return false;

		return true;
	}

	bool be_encode(char* s_out, int* len)
	{
		if(!s_out || len) return false;

		*len = sprintf(s_out, "l%lle", elem_);
		return true;
	}

	void dump()
	{
		printf("int = %lli\n", elem_);
	}
};

class be_string : public be_object
{
public:
	std::string* elem_;
public:
	virtual void dump()
	{
		printf("str = %s\n", elem_->c_str());
	}

	be_string(){ type_ = BE_STR; }
	be_string(const char*s) : elem_(new string(s)){ type_ = BE_STR; }
	bool be_decode(const char* s_in, char** s_next)
	{
		if(!s_in || !s_next) return false;

		be_integer i;
		i.be_decode(s_in, s_next);
		long long len = i.elem_;
		elem_ = new string(*s_next, len);
		return true;
	}

	bool be_encode(char* s_out, int* len)
	{
		if(!s_out || !len) return false;

		*len = sprintf(s_out, "%lli%s", elem_->size(), elem_->c_str());
		return true;
	}
};

class be_dict : public be_object
{
public:
	std::map<be_string*, be_object*> elem_;
public:
	be_dict(){ type_ = BE_DICT; }

	bool be_decode(const char* s_in, char** s_next)
	{
		if (!s_in || !s_next) return false;

		const char *pstr = s_in;// char *next;
		while (*s_in != 'e')
		{
			be_string *s = new be_string;
			if (!s->be_decode(pstr, s_next))
				return false;
			pstr = *s_next;

			be_object *o = be_factory::create_be_object(pstr);
			if (!o || !o->be_decode(pstr, s_next))
				return false;
			pstr = *s_next;

			elem_.insert(make_pair(s, o));
		}

		return true;
	}

	bool be_encode(char* s_out, int* len)
	{
		if (!s_out || !len) return false;

		char *pstart = s_out;
		*s_out++ = 'd';

		std::map<be_string*, be_object*>::const_iterator iter;
		for (iter = elem_.begin(); iter != elem_.end(); iter++)
		{
			iter->first->be_encode(s_out, len);
			s_out += *len;

			iter->second->be_encode(s_out, len);
			s_out += *len;
		}

		*s_out++ = 'e';
		*len = s_out - pstart;

		return true;
	}

	void dump()
	{
		puts("dict {");

		std::map<be_string*, be_object*>::const_iterator iter;
		for (iter = elem_.begin(); iter != elem_.end(); iter++)
		{
	//		printf("%s => ", iter->first->dump());
			iter->first->dump();
			printf(" => ");

			iter->second->dump();
		}
	}
};

class be_list : public be_object
{
public:
	std::vector<be_object*> elem_;
public:
	be_list(){ type_ = BE_LIST; }

	bool be_decode(const char* s_in, char** s_next)
	{
		if (!s_in || !s_next) return false;

		while (*s_in != 'e')
		{
			be_object *o = be_factory::create_be_object(s_in);
			if (!o || !o->be_decode(s_in, s_next))
				return false;
			s_in = *s_next;

			elem_.push_back(o);
		}

		return true;
	}

	bool be_encode(char* s_out, int* len)
	{
		if (!s_out || !len) return false;

		char *pstart = s_out;
		*s_out++ = 'l';

		std::vector<be_object*>::const_iterator iter;
		for (iter = elem_.begin(); iter != elem_.end(); iter++)
		{
			(*iter)->be_encode(s_out, len);
			s_out += *len;
		}

		*s_out++ = 'e';
		*len = s_out - pstart;

		return true;
	}
	
	virtual void dump()
	{
		printf("list => {");
		for (std::vector<be_object*>::const_iterator iter = elem_.begin();
			iter != elem_.end(); ++iter) 
		{
			(*iter)->dump();
			printf(", ");
		}
		printf("\n}");
	}
};

class Utility
{
public:
	static bool Hex2Literal(unsigned char* dest, unsigned char src)
	{
		if (NULL == dest)
			return false;

		char *table = "0123456789ABCDEF";

		*dest++ = *(table + ((src & 0xF0) >> 4));
		*dest = *(table + (src >> 4));

		return true;
	}

	static string Hex2str(const unsigned char* src, const size_t size)
	{
		string str_ret = "";
		if (NULL == src || 0 == size) {
			return str_ret;
		}

		auto_ptr<unsigned char> tmp(new unsigned char[(size << 1) + 1]);
		unsigned char* p = tmp.get();
		for (size_t i = 0; i < size; ++i) {
			Hex2Literal(p, src[i]);
			p += 2;
		}
		*p = '0';

		return string((char*)tmp.get());
	}
	
	static unsigned char Literal2Hex(unsigned char c)
	{
		unsigned char tmp = (unsigned char)-1;
		
		if (c >= '0' && c <= '9') {
			tmp = c - '0';
		} else if (c >= 'A' && c <= 'F') {
			tmp = c - 'A' + 10;
		} else if (c >= 'a' && c <= 'f') {
			tmp = c - 'a' + 10;
		}

		return tmp;
	}

	static bool Literal2Hex(unsigned char& dest, const unsigned char* src)
	{
		if (NULL == src)
			return false;

		unsigned char tmp = Literal2Hex(*src);
		if (tmp == (unsigned char)-1) {
			return false;
		}

		dest = tmp << 4;
		src++;
		tmp = Literal2Hex(*src);
		if (tmp == (unsigned char)-1) {
			return false;
		}
		dest = dest | tmp;

		return true;
	}

	static bool Str2Hex(unsigned char* dest, size_t& size, const string& src)
	{
		if (NULL == dest ) {
			return false;
		}

		size = src.size() >> 1;
		for (size_t i = 0; i < size; ++i) {
			Literal2Hex(*dest++, (unsigned char*)&src[i << 1]);
		}
		
		return true;
	}
	
	int calc_piece_size(unsigned long long filesize)
	{
		if (filesize == 0)
			return 0;
		
		const int max_piece_size = (2<<20); // 2M
		const int max_piece_count = 512<<10; //512K
		int piece_size = 256;
		int piece_count = (int)((filesize + piece_size - 1) / piece_size);
		
		while ((piece_size < max_piece_size) && (piece_count > max_piece_count)) {
			piece_size <<= 1;
			piece_count = (int)((filesize + piece_size - 1) / piece_size);
		}
		
		return piece_size;
	}
	
	int calc_piece_count(unsigned long long filesize)
	{
		int piece_count = 0;
		int piece_size = calc_piece_size();
		if (piece_size > 0) {
			piece_count = (int)((filesize + piece_size - 1) / piece_size);
		}
		
		return piece_count;
	}
	
	static void UnicodeToUTF8(const std::basic_string<wchar_t>& in, std::basic_string<char>& out)
	{
		int len = (int)in.length();
		char* pBuf = new char[len * 3 + 1];
		memset(pBuf, 0, len * 3 + 1);
		::WideCharToMultiByte(CP_UTF8, 0, in.c_str(), len, pBuf, len * 3, NULL, NULL);
		out = pBuf;
		delete [] pBuf;
		return;
	}

	static void UTF8ToUnicode(const std::basic_string<char>& in, std::basic_string<wchar_t>& out)
	{
		int len = (int)in.length();
		wchar_t* pBuf = new wchar_t[len + 1];
		memset(pBuf, 0, (len + 1) * sizeof(wchar_t));
		::MultiByteToWideChar(CP_UTF8, 0, in.c_str(), len, pBuf, len * sizeof(wchar_t));
		out = pBuf;
		delete [] pBuf;
	}
/*
	static void GBKToUTF8(const std::string& in, std::string& out)
	{
		//char (GBK) to UTF-8
		//GBK->wchar_t
		int wbufferlen = (int)::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,in.c_str(),(int)in.size(),NULL,0);
		wchar_t* pwbuffer = new wchar_t[wbufferlen+4];
		wbufferlen = (int)::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,in.c_str(),(int)in.size(),pwbuffer,wbufferlen+2);
		//wchar_t -> UTF8
		int bufferlen = ::WideCharToMultiByte(CP_UTF8,0,pwbuffer,wbufferlen,NULL,0,NULL,NULL);
		char* pBuffer = new char[bufferlen + 4];
		::WideCharToMultiByte(CP_UTF8,0,pwbuffer,wbufferlen,pBuffer,bufferlen+2,NULL,NULL);
		pBuffer[bufferlen] = '\0';
		out = pBuffer;
		delete[] pwbuffer;
		delete[] pBuffer;
		return;
	}

	static void UTF8ToGBK(const std::string& in, std::string& out)
	{
		//UTF-8 to char (GBK)

		//1) UTF-8 -> whcar_t
		int wbufferlen = (int)::MultiByteToWideChar(CP_UTF8,0,in.c_str(),(int)in.size(),NULL,0);    //may cause error
		wchar_t* pwbuffer = new wchar_t[wbufferlen+4];
		wbufferlen = (int)::MultiByteToWideChar(CP_UTF8,0,in.c_str(),(int)in.size(),pwbuffer,wbufferlen+2);
		//2) wchar_t -> GBK
		int bufferlen = (int)::WideCharToMultiByte(CP_ACP,0,pwbuffer,wbufferlen,NULL,0,NULL,NULL);
		char* pBuffer = new char[bufferlen + 4];
		::WideCharToMultiByte(CP_ACP,0,pwbuffer,wbufferlen,pBuffer,bufferlen+2,NULL,NULL);   
		pBuffer[bufferlen] = '\0';
		out = pBuffer;
		delete[] pwbuffer;
		delete[] pBuffer;
		return;
	}*/
};

}

#endif
